home *** CD-ROM | disk | FTP | other *** search
- static char rcsid[] = "$Id: led.c,v 1.2 1992/11/10 20:04:22 mike Exp $";
-
- /* $Log: led.c,v $
- * Revision 1.2 1992/11/10 20:04:22 mike
- * - Changed according to new version of `Eget_key'.
- *
- * Revision 1.1 1992/09/14 13:02:14 mike
- * Initial revision
- *
- */
-
- /* LED.c : Line Editor library
- * External routines:
- * t_open() : Open terminal. Probably set Lncol. If terminal width
- * changes, you'll need to manage Lvline and Lpline.
- * t_close() : Close terminal.
- * t_putchar() : Handles BS, CR and NL. No echo, ^C trapping.
- * t_getchar() : No echo.
- * t_flush() : Flush the terminal.
- * t_clearline(n) : Put the cursor at the start of a blank line.
- * n == 0: Put the cursor at the start of a clear line.
- * n == 1: Move the cursor to start of the line.
- * Returns: Same as n.
- * Note: If you can't do n==1, pretend n==0 and return 0;
- * Variables of Interest:
- * Lncol: Number columns per line MINUS 1 or 2.
- * Lpline, Lvline: pointers to char arrays that hold at least Lncol chars
- * each. If Lncol ever changes, make sure the arrays track.
- * Lflags: Control some internal stuff.
- * LFnohist : if set, don't insert test into the history stack. Default
- * is 0.
- * LFrecdraw : if set and led recurses, don't redraw the line. Default is
- * 0. Used if you want the new prompt to overwrite the current line.
- * Returns:
- * Led() return code: Same as Ldo_fcn().
- * Ldo_fcn() return codes:
- * LF_DONE: User said all done now - process my input. Ltext points
- * the entered text. NOTE: Ltext is only good on LF_DONE. It may
- * change from call to call. It is buffer local. Text in buffer is
- * only good till next call to Led() or Ldo_fcn();
- * LF_ABORT: User wants to stop - ignore this and get me out.
- * LF_OK: Command(s) executed OK.
- * LF_ERROR: Something bad happened. Lerrorno has the error number.
- * Led(prompt,prefload,callback)
- * callback(kc,n): When a key is hit that is bound to LF_CALLBACK and
- * callback is != NULL, callback(kc,n) is called (where kc is the key
- * pressed and n is what key was bound to). Return code sez what to do:
- * return LF_NOOP if nothing is to be done, LF_DONE to simulate the user
- * pressing LF_DONE, etc. Normally, just "return Ldo_fcn(...);".
- * Recursion is OK.
- * Global callbacks:
- * Lregister_callback(0, (pfi)foo); foo(kc,n,op) will be called for any
- * key that is bound to LF_EXTEND. If foo returns TRUE, no other
- * callbacks will be called and Ldo_fcn() will be called for what ever
- * is in op. If foo return FALSE, the next callback in the list will be
- * called.
- * Ldo_fcn(list-of-functions-to-do)
- * Execute a bunch of functions. The list is terminated be LF_STOP.
- * For example:
- * Ldo_fcn(LF_CLEAR_LINE, LF_INSERT_STRING,"hello world", LF_STOP);
- * This replaces the line with "hello world".
- * Ldo_fcn(LF_BINDKEY, LF_RIGHT, CTRL|'F', LF_STOP, LF_STOP);
- * This binds move-cursor-right to control-F. Note the need for two
- * LF_STOPs - one to terminate the key binding list and one to
- * termainate the Ldo_fcn list.
- * See led.h for list of functions.
- * Ldot : return where the cursor is in Ttext.
- * Lline_length : return the strlen of Ttext.
- * C Durland 1990
- */
-
- /*
- * Other ideas:
- * A command que for things like signal handlers (^C and the like).
- * Make sure buffers Line is inited at init time.
- */
-
- /*
- * how to use:
- * t_open();
- * [set Lncol, Lvline, Lpline]
- * init_led();
- * while(something)
- * {
- * Led(....);
- * do something with results
- * }
- * [t_close();]
- * exit();
- */
-
- /*
- * Implementation notes:
- * The Line in the Buffer is a bunch of characters with a length. The
- * line ALWAYS has enough room at the end for a \0 to turn it into a C
- * string.
- * The first thing Led() does is set the prompt. This will make sure that
- * the line has space in it (even if prompt is "").
- * line_is_garbage :
- * 0 : physical line is NOT garbage.
- * 1 : physical line is garbage.
- * 2 : pline is in sync but hardware cursor is out of sync.
- */
-
- /* Copyright 1990, 1991 Craig Durland
- * Distributed under the terms of the GNU General Public License.
- * Distributed "as is", without warranties of any kind, but comments,
- * suggestions and bug reports are welcome.
- */
-
- static char what[] = "@(#)LED (Line EDitor) v1.0 5/2/90";
-
- #include <dtable.h>
- #include <const.h>
- #include "ed.h"
- #include "led.h"
-
- #ifdef atarist
- #include "../me2/stio.h"
- #endif
-
- #include <char.h> /* for iscntrl() only */
-
- #ifdef __STDC__
-
- #include <stdarg.h>
- #define VA_START va_start
-
- #else /* __STDC__ */
-
- #include <varargs.h>
- #define VA_START(a,b) va_start(a)
-
- #endif
-
- /* ******************************************************************** */
- /* ************* Types and Structures ********************************* */
- /* ******************************************************************** */
-
- typedef declare_dTable_of(char) Line;
- #define LINE_LENGTH sizeof_dTable(lp)
- #define TEXT (lp->table)
-
- typedef struct /* keys bound to programs */
- {
- KeyCode keycode; /* Key code used to invoke it */
- char fcn; /* fcn to do */
- short int x; /* for callbacks, ?? */
- } Key;
-
- typedef declare_dTable_of(Key) KeyTable;
-
- /* ******************************************************************** */
- /* ************* Extern Routines ************************************** */
- /* ******************************************************************** */
-
- extern char *malloc();
-
- static int grok_key(), push_line(), pop_line();
- static void LEDkeys(), movecursor(), update(), updateline();
-
-
- /* ******************************************************************** */
- /* ************* Global Variables ************************************* */
- /* ******************************************************************** */
-
- char
- *Lpline = NULL, *Lvline = NULL,
- *Ltext; /* the text the user input */
- int
- Lerrorno, /* LEDs error number */
- Lncol,
- Lflags;
-
- /* ******************************************************************** */
- /* ************* Variables ******************************************** */
- /* ******************************************************************** */
-
- static int
- lmargin, line_is_garbage, dot, prompt_len,
- hstep, ttcol;
- static pfi Lcallback;
-
- static KeyCode keypressed;
- static Line text = initial_dTable_data(text), *lp = &text;
-
- static declare_pkeys(pkeys,SHIFT,SHIFT,SHIFT,SHIFT);
-
- static KeyTable gkeys = initial_dTable_data(gkeys); /* global key table */
-
- /* ******************************************************************** */
- /* ******************************************************************** */
- /* ******************************************************************** */
-
- #ifdef __STDC__
- int Ldo_fcn(int fcn, ...);
- #endif
-
- /* t_open() has been called */
- init_Led()
- {
- extern void init_linestuff();
-
- if (!Lpline) Lpline = malloc(Lncol);
- if (!Lvline) Lvline = malloc(Lncol);
- hstep = Lncol/3;
-
- LEDkeys();
- Lflags = 0;
- init_linestuff();
- return TRUE;
- }
-
- #define INSERT 0
- #define CALLBACK 1
- #define DOFCN 2
- #define DOZIP 3
-
- Led(prompt,preload,callback)
- char *prompt, *preload;
- pfi callback;
- {
- static int rlevel = 0;
-
- int s, f;
- Key *key;
-
- f = (rlevel && (Lflags & LFrecdraw)) ? LF_NOOP : LF_REDRAW;
-
- if (rlevel++) push_line(); /* we are recursing */
- else Htop(); /* A first time call */
-
- Lcallback = callback;
-
- s = Ldo_fcn(LF_PROMPT,prompt,LF_INSERT_STRING,preload,
- f,LF_UPDATE,LF_STOP);
- while (s != LF_DONE && s != LF_ABORT && s != LF_ERROR)
- {
- key = (Key *)Efind_key(&gkeys,keypressed = Eget_key(pkeys,0L));
- switch (grok_key(key,keypressed))
- {
- case INSERT:
- s = Ldo_fcn(LF_INSERT_KEYCODE,(int)keypressed,LF_UPDATE,LF_STOP);
- break;
- case DOFCN:
- s = Ldo_fcn(key->fcn,LF_UPDATE,LF_STOP);
- break;
- case CALLBACK:
- s = Ldo_fcn(key->fcn,key->x,LF_UPDATE,LF_STOP);
- break;
- }
- }
- if (--rlevel) pop_line(); /* we were recursing */
- return s;
- }
-
- static int grok_key(key,kc) Key *key; KeyCode kc;
- {
- if (key)
- {
- if (key->fcn != LF_CALLBACK && key->fcn != LF_EXTEND) return DOFCN;
- if ((key->fcn == LF_CALLBACK && Lcallback) ||
- (key->fcn == LF_EXTEND)) return CALLBACK;
- }
- if ((kc & 0xFF00) == 0) return INSERT;
- return DOZIP;
- }
-
- /* ******************************************************************** */
- /* ******************************************************************** */
- /* ******************************************************************** */
-
- static int do_callbacks(), bind_key(), linsert();
- static void set_Ltext();
-
- /*VARARGS1*/
- #ifdef __STDC__
- Ldo_fcn(int fcn, ...)
- #else
- Ldo_fcn(fcn, va_alist) int fcn; va_dcl
- #endif
- {
- char tmp[20], *ptr;
- register KeyCode kc;
- register int n;
- va_list ap;
-
- VA_START(ap,fcn);
- while (TRUE)
- {
- execute_fcn:
- switch (fcn)
- {
- case LF_NOOP: break; /* also LF_OK. no op is a easy thing to do */
- case LF_DONE: /* done with this line, give it back */
- set_Ltext();
- if (!(Lflags & LFnohist)) Hadd(Ltext);
- alldone:
- va_end(ap);
- return fcn;
- case LF_ERROR: /* Assumes Lerrorno set. Useful for callbacks. */
- error: fcn = LF_ERROR; goto alldone;
- case LF_ABORT: goto alldone;
- case LF_STOP: fcn = LF_OK; goto alldone;
-
- case LF_BoL: /* begining of line */
- dot = prompt_len;
- lmargin = 0; /* make sure line updates correctly */
- break;
- case LF_RIGHT: /* right arrow */
- if (dot < LINE_LENGTH) dot++; break;
- case LF_LEFT: /* left arrow */
- if (prompt_len<dot) { dot--; if (dot==prompt_len) lmargin = 0; }
- break;
- case LF_EoL: dot = LINE_LENGTH; break; /* end of line */
-
- case LF_DEL_CHAR: /* delete character in place */
- if (dot < LINE_LENGTH)
- {
- for (n = dot; n < LINE_LENGTH; n++) TEXT[n] = TEXT[n+1];
- LINE_LENGTH--;
- }
- break;
- case LF_EEoL: LINE_LENGTH = dot; break; /* clear to end of line */
- case LF_CLEAR_LINE: /* clear the entire line */
- Ldo_fcn(LF_BoL,LF_EEoL,LF_STOP);
- break;
- case LF_BS: /* destrutive backspace */
- if (prompt_len < dot) Ldo_fcn(LF_LEFT,LF_DEL_CHAR,LF_STOP);
- break;
- case LF_REDRAW: line_is_garbage = 1; break; /* redraw line */
- case LF_CTYNC: line_is_garbage = 2; break; /* sync cursor */
- case LF_UPDATE: update(); break;
- case LF_TAB: /* insert a tab */
- Ldo_fcn(LF_INSERT_KEYCODE,'\t',LF_STOP);
- break;
- case LF_QUOTE: /* C-Q: quote next char */
- kc = t_getchar();
- Ldo_fcn(LF_INSERT_KEYCODE,kc,LF_STOP);
- break;
- case LF_UQUOTE: /* \: UNIX quote next char */
- Ldo_fcn(LF_INSERT_KEYCODE,keypressed,LF_UPDATE,LF_STOP);
- kc = t_getchar();
- if (
- ((kc & 0xFF00)==0 && iscntrl(kc)) ||
- Efind_key(&gkeys,Ectok(kc,FALSE))) Ldo_fcn(LF_BS,LF_STOP);
- Ldo_fcn(LF_INSERT_KEYCODE,kc,LF_STOP);
- break;
- case LF_INSERT_KEYCODE:
- kc = va_arg(ap,int /*KeyCode*/);
- *tmp = '\0'; Expand_key(pkeys,kc,tmp); /* make the keycode printable */
- if (!linsert(tmp)) goto error;
- break;
- case LF_INSERT_STRING:
- ptr = va_arg(ap,char *);
- if (!linsert(ptr)) goto error;
- break;
-
- case LF_PROMPT: /* set a new prompt */
- ptr = va_arg(ap,char *);
- LINE_LENGTH = dot = 0; if (!linsert(ptr)) goto error;
- prompt_len = strlen(ptr);
- Ldo_fcn(LF_BoL,LF_STOP);
- break;
- case LF_BINDKEY: /* (bindkey fcn keycode ... LF_STOP) */
- while (TRUE)
- {
- fcn = va_arg(ap,int);
- if (fcn == LF_STOP) break;
- if (fcn==LF_CALLBACK || fcn==LF_EXTEND) /* (bind callback n kc) */
- n = va_arg(ap,int);
- kc = va_arg(ap,int /*KeyCode*/);
- if (!bind_key(kc,fcn,n)) goto error;
- }
- break;
- case LF_PKEY: /* (PKEY n keycode ... LF_STOP) */
- while (TRUE)
- {
- n = va_arg(ap,int);
- if (n == LF_STOP) break;
- kc = va_arg(ap,int /*KeyCode*/);
- if (!Eset_pkey(pkeys,n,kc)) { Lerrorno = LEbound; goto error; }
- }
- break;
- case LF_CLEAR_KEYMAP: clear_keytable(&gkeys); break;
- case LF_CALLBACK: /* (callback n) */
- fcn = va_arg(ap,int);
- if (!Lcallback) break;
- set_Ltext();
- fcn = (*Lcallback)(keypressed,fcn);
- goto execute_fcn;
- case LF_EXTEND: /* (extend n) */
- fcn = va_arg(ap,int);
- fcn = do_callbacks(keypressed,fcn);
- goto execute_fcn;
- default:
- Lerrorno = LEunknown_fcn;
- goto error;
- }
- fcn = va_arg(ap,int);
- }
- /* never gets here */
- }
-
- static void set_Ltext()
- {
- TEXT[LINE_LENGTH] = '\0'; /* know there is room */
- Ltext = &TEXT[prompt_len];
- }
-
- Ldot() { return dot -prompt_len; }
- Lline_length() { return LINE_LENGTH -prompt_len; }
-
- /* ******************************************************************** */
- /* ************************* Global Callbacks ************************* */
- /* ******************************************************************** */
-
- #define MAX_CALLBACKS 5
-
- static pfi callback_list[MAX_CALLBACKS];
- static int num_callbacks = 0;
-
- Lregister_callback(callback_num, callback) pfi callback;
- {
- if (num_callbacks == MAX_CALLBACKS) return FALSE;
-
- callback_list[num_callbacks++] = callback;
-
- return TRUE;
- }
-
- /* Call the callbacks in reverse order so that the last registered will
- * have the first crack at the fcn.
- */
- static int do_callbacks(kc, fcn) KeyCode kc; int fcn;
- {
- int j, op;
-
- set_Ltext();
- for (j = num_callbacks; j--; )
- if ( (*callback_list[j])(kc, fcn, &op)) return op;
-
- return LF_NOOP; /* nobody wanted the callback */
- }
-
- /* ******************************************************************** */
- /* *************** Keys *********************************************** */
- /* ******************************************************************** */
-
- static void LEDkeys()
- {
- Ldo_fcn(LF_CLEAR_KEYMAP,LF_BINDKEY,
- LF_BS, CTRL|'H', /* backspace */
- LF_TAB, CTRL|'I', /* tab */
- LF_DEL_CHAR, CTRL|'?', /* DEL(7F) == ^? == ^D */
- LF_ABORT, CTRL|'C', /* exit */
- LF_DONE, CTRL|'M', /* enter or CR */
- LF_DONE, CTRL|'J', /* newline */
- LF_UQUOTE, '\\', /* UNIX style quote */
- LF_REDRAW, CTRL|'L', /* refresh-screen */
- LF_CLEAR_LINE, CTRL|'U',
-
- /* softkeys */
- LF_RIGHT, SOFKEY|'E', /* next-character : right arrow */
- LF_LEFT, SOFKEY|'F', /* previous-character : left arrow */
- LF_DEL_CHAR, SOFKEY|'H', /* delete-char : delete */
-
- LF_STOP, LF_STOP);
- }
-
- static int bind_key(keycode,fcn,x) KeyCode keycode; int fcn, x;
- {
- Key *key;
-
- if (fcn == LF_UNDEF) { Eunbind_key(&gkeys,keycode); return TRUE; }
- if (!(key = (Key *)Ekalloc(&gkeys,keycode)))
- { Lerrorno = LEmem; return FALSE; }
- key->keycode = keycode; key->fcn = fcn; key->x = x;
- return TRUE;
- }
-
- /* ******************************************************************** */
- /* ******************* Allocate Lines ********************************* */
- /* ******************************************************************** */
-
- #define LINES 10
- typedef struct
- {
- int lmargin, dot, prompt_len;
- pfi Lcallback;
- Line line, *lp;
- } LineStuff;
-
- static LineStuff lines[LINES];
- static int nth_line = 0;
-
- void init_linestuff()
- {
- int j;
-
- for (j = LINES; j--; ) INIT_dTable(&lines[j].line);
- }
-
- /* save context of current line and set up a new line
- * New line inited by Led().
- * !!!!!!!!!!!assumes that lines[] is initied!!!!!!!!!!!!!!!!
- * Can't init line because it might have been used before and have
- * allocated some data.
- */
- static int push_line()
- {
- LineStuff *lsp;
-
- if (nth_line == LINES) return FALSE;
- lsp = &lines[nth_line++];
-
- lsp->lmargin = lmargin;
- lsp->dot = dot;
- lsp->prompt_len = prompt_len;
- lsp->Lcallback = Lcallback;
- lsp->lp = lp;
-
- lp = &lsp->line;
- reset_dTable(lp);
- return TRUE;
- }
-
- /* restore last used line */
- static int pop_line()
- {
- LineStuff *lsp;
-
- lsp = &lines[--nth_line];
-
- lmargin = lsp->lmargin;
- dot = lsp->dot;
- prompt_len = lsp->prompt_len;
- Lcallback = lsp->Lcallback;
- lp = lsp->lp;
- return TRUE;
- }
-
- /* ******************************************************************** */
- /* **************** Manage Contents of Lines ************************** */
- /* ******************************************************************** */
-
- /* blkmovm(to,from,n)
- * F = source, T = dest
- * |---------**from*F----------|
- * |---------------***to**T----|
- */
-
-
- /* Open a hole of size n @ dot in a line. */
- static void hole(line,dot,n) Line *line; int dot, n;
- {
- register char *ptr, *qtr;
- register int x;
-
- qtr = &line->table[sizeof_dTable(line) -1];
- ptr = qtr +n;
- x = sizeof_dTable(line) -dot;
- while (x--) *ptr-- = *qtr--;
- }
-
- static char deadline[1] = { '\0' };
-
- static int linsert(text) char *text;
- {
- int n = strlen(text), a = sizeof_dTable(lp);
-
- if (!xpand_dTable(lp,n+1,32,32))
- { lp->table = deadline; Lerrorno = LEmem; return FALSE; }
- sizeof_dTable(lp) = a; hole(lp,dot,n); sizeof_dTable(lp) = a+n;
- memcpy(&lp->table[dot], text, n);
- dot += n;
- return TRUE;
- }
-
- static int getccol(text,dot) register char *text;
- {
- register char c;
- register int i, col = 0;
-
- for (i = dot; i--;)
- {
- c = *text++;
- if (c == '\t') col = NEXT_TAB_COLUMN_MINUS_1(col);
- else if (iscntrl(c)) ++col;
- ++col;
- }
- return col;
- }
-
- /* ******************************************************************** */
- /* ******************* Video routines ********************************* */
- /* ******************************************************************** */
-
- /* Format a screen line: convert tabs to spaces, control characters to
- * printable. Right pad with blanks. Shift text left as needed to fit it
- * between the screen margins. You have to look at the text from the
- * beginning to ensure correct tab and control character expansion.
- */
- #define ADD_CHAR(c) if (lmargin <= col++) *qtr++ = c
- static void vblast(ptr,len,lmargin) unsigned char *ptr; register int lmargin;
- {
- register unsigned char *qtr = (unsigned char *)Lvline;
- register int col = 0;
- register unsigned char c;
- register int rmargin = lmargin +Lncol;
-
- while (col < rmargin && len--)
- {
- c = *ptr++;
- if (iscntrl(c))
- {
- if (c == '\t') /* expand tab */
- {
- register int n = imin(rmargin, NEXT_TAB_COLUMN(col));
-
- do { ADD_CHAR(' '); } while (col < n);
- continue;
- }
- ADD_CHAR('^'); /* LineFeed => "^J" */
- c ^= 0x40; /* Convert ASCII LineFeed to "J" */
- if (col == rmargin) continue; /* no room for the "J" */
- /* fall though and put the "J" in the video */
- }
- ADD_CHAR(c);
- }
- if (0 < len) *(qtr-1) = '$'; /* hit rmargin before wrote all of row */
- else /* clear to end of line */
- memset(qtr,' ',(col <= lmargin) ? Lncol : rmargin -col);
-
- if (0 < lmargin) Lvline[0] = '$';
- }
-
- /*
- * Make sure that the display is right. This is a three part process:
- * First, scan through all of the windows looking for dirty ones. Check
- * the framing, and refresh the screen.
- * Second, make sure that "currow" and "curcol" are correct for the
- * current window.
- * Third, make the virtual and physical screens the same.
- */
- static void update()
- {
- register int j, k, curcol;
-
- curcol = getccol(TEXT,dot); /* find the cursor column */
- if (hstep!=0) /* check to see if have to horizontal scroll */
- {
- k = lmargin;
- if (curcol < k) /* cursor is left of window margin */
- {
- j = ((k -curcol +hstep -1)/hstep)*hstep;
- if ((k -= j)<0) k = 0;
- }
- else
- if (curcol >= k+Lncol) /* cursor is right of window */
- {
- j = ((curcol -(k +Lncol) +hstep)/hstep)*hstep;
- k += j;
- }
- lmargin = k;
- }
-
- vblast(TEXT,LINE_LENGTH,lmargin);
-
- /* Special hacking if the screen is garbage. Clear the hardware screen,
- * and update your copy to agree with it. Set all the virtual screen
- * change bits, to force a full update.
- */
- if (line_is_garbage)
- {
- j = t_clearline(line_is_garbage-1);
- if (j == 0) memset(Lpline,' ',Lncol);
- line_is_garbage = 0;
- ttcol = 0;
- }
-
- updateline(Lvline,Lpline);
-
- /* Finally, update the hardware cursor and flush out buffers */
- j = curcol -lmargin;
- if (Lncol <= j) j = Lncol-1; /* cursor off right side of screen */
- else if (j < 0) j = 0; /* cursor off left side of screen */
- movecursor(j);
- t_flush();
- }
-
- /*
- * Update a single line. This does not know how to use insert or delete
- * character sequences; we are using Backspace functionality. Update the
- * physical row and column variables.
- */
- static void updateline(vline,pline) char *vline, *pline;
- {
- register char
- *cp1 = vline, *cp2 = pline, *cp4,
- *cp3 = &vline[Lncol], *cp5 = cp3;
-
- /* Compute left match */
- while (cp1 != cp5 && *cp1 == *cp2) { ++cp1; ++cp2; }
-
- if (cp1 == cp5) return; /* lines are equal */
-
- /* Compute right match */
- cp4 = &pline[Lncol];
- while (cp3[-1] == cp4[-1]) { --cp3; --cp4; }
-
- cp5 = cp3;
- movecursor(cp1-vline);
- while (cp1 != cp5) /* write out mismatched part */
- { t_putchar(*cp1); ++ttcol; *cp2++ = *cp1++; }
- }
-
- #define BS 0x08 /* BackSpace */
-
- /*
- * Send a command to the terminal to move the hardware cursor to
- * (row,column). Can only use BS and write;
- * The row and column arguments are origin 0.
- * Optimize out random calls.
- * Update "ttcol".
- */
- static void movecursor(col) register int col;
- {
- register char *ptr;
-
- /* move cursor left with backspace */
- while (col < ttcol) { ttcol--; t_putchar(BS); }
- if (ttcol < col) /* move cursor right with write */
- {
- ptr = &Lvline[ttcol];
- while (ttcol < col) { ttcol++; t_putchar(*ptr++); }
- }
- }
-